调用分布式服务接口时经常会遇到这样的问题:接口方提供多个 IP 供用户调用,只要有一个返回成功就算成功。
对于这样的问题,一个比较简单的方案是依次调用各个接口,如果前一个接口未成功返回再调用第二个接口。这样做的好处是对于服务器资源消耗比较小,但对于用户来说效率非常低下。设想调用第一个接口经过 20 秒超时出错才调用第二个接口,如果第二个接口又是 20 秒超时,用户就已经等待了 40 秒。用户的等待时间按线性增长,这样的结果时不可接受的。
好的解决方案是同时并发调用所有接口,有些同学可能会想到 Promise.race
。但是请注意:Promise.race 不是等到有一个 Promise 被 resolve 时返回,而是只要有一个 Promise 被 fulfill 时就会返回,不论这个 Promise 是 resolve 还是 reject。
我就遇到过类似问题,当然也被 。上谷歌搜到了老外这篇博文:Promise me you won’t use Promise.race。博文中详细探讨了这个问题,并用 Promise.race
坑害了一回Promise.race
实现了一个解决方案:
Promise.properRace = function properRace(promises) {
if (promises.length < 1) {
return Promise.reject('Can\'t start a race without promises!');
}
// There is no way to know which promise is rejected.
// So we map it to a new promise to return the index when it fails
const indexPromises = promises.map((p, index) => p.catch(e => {
console.debug('Promise rejected in `properRace`: ' + e);
return Promise.reject(index);
}));
return Promise.race(indexPromises).catch(index => {
// The promise has rejected, remove it from the list of promises and just continue the race.
promises.splice(index, 1)[0].catch(() => { /* eat this */ });
return promises.length ? properRace(promises) : Promise.reject('All promises rejected');
});
};
虽然略显复杂,但方法确实很巧妙,我初期就使用了这种方法。最近回过头来突然想到:properRace
的需求是只要有一个被 resolve 返回的 Promise 就被 resolve;JavaScript 虽然没有提供这样的函数,但是提供了另外一个很相似的函数:只要有一个被 reject 返回的 Promise 就被 reject。
这个函数我们很常用,就是:Promise.all
想到这一层,那么用它来实现 properRace 就很简单了:把传入的 Promise 数组状态正反对调就好了。结果如下:
Promise.properRace = function properRace(promises) {
const resolve = Promise.resolve.bind(Promise);
const reject = Promise.reject.bind(Promise);
return Promise.all(promises.map(x => x.then(reject, resolve)))
.then(reject, resolve);
}
完
2019年7月更新
此方法已经被标准化为 Promise.any
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。